Izpētiet TypeScript zīmola tipus – jaudīgu tehniku nominālās tipēšanas sasniegšanai strukturālā tipu sistēmā. Uzziniet, kā uzlabot tipu drošību un koda skaidrību.
TypeScript zīmola tipi: Nominālā tipēšana strukturālā sistēmā
TypeScript strukturālā tipu sistēma piedāvā elastību, bet dažkārt var novest pie neparedzētas uzvedības. Zīmola tipi sniedz veidu, kā ieviest nominālo tipēšanu, uzlabojot tipu drošību un koda skaidrību. Šajā rakstā detalizēti aplūkoti zīmola tipi, sniedzot praktiskus piemērus un labākās prakses to ieviešanai.
Strukturālās un nominālās tipēšanas izpratne
Pirms iedziļināties zīmola tipos, noskaidrosim atšķirību starp strukturālo un nominālo tipēšanu.
Strukturālā tipēšana (Duck Typing)
Strukturālā tipu sistēmā divi tipi tiek uzskatīti par saderīgiem, ja tiem ir vienāda struktūra (t.i., vienādas īpašības ar vienādiem tipiem). TypeScript izmanto strukturālo tipēšanu. Apskatīsim šādu piemēru:
interface Point {
x: number;
y: number;
}
interface Vector {
x: number;
y: number;
}
const point: Point = { x: 10, y: 20 };
const vector: Vector = point; // Derīgs TypeScript
console.log(vector.x); // Izvade: 10
Lai gan Point
un Vector
ir deklarēti kā atsevišķi tipi, TypeScript ļauj piešķirt Point
objektu Vector
mainīgajam, jo tiem ir vienāda struktūra. Tas var būt ērti, bet var arī novest pie kļūdām, ja nepieciešams atšķirt loģiski dažādus tipus, kuriem gadās būt vienādai formai. Piemēram, domājot par platuma/garuma grādu koordinātām, kas varētu nejauši sakrist ar ekrāna pikseļu koordinātām.
Nominālā tipēšana
Nominālā tipu sistēmā tipi tiek uzskatīti par saderīgiem tikai tad, ja tiem ir vienāds nosaukums. Pat ja diviem tipiem ir vienāda struktūra, tie tiek uzskatīti par atšķirīgiem, ja tiem ir dažādi nosaukumi. Tādas valodas kā Java un C# izmanto nominālo tipēšanu.
Nepieciešamība pēc zīmola tipiem
TypeScript strukturālā tipēšana var radīt problēmas, ja nepieciešams nodrošināt, ka vērtība pieder noteiktam tipam, neatkarīgi no tās struktūras. Piemēram, apsveriet valūtu attēlošanu. Jums varētu būt dažādi tipi USD un EUR, bet abi varētu tikt attēloti kā skaitļi. Bez mehānisma to atšķiršanai, jūs varētu nejauši veikt darbības ar nepareizu valūtu.
Zīmola tipi risina šo problēmu, ļaujot jums izveidot atšķirīgus tipus, kas ir strukturāli līdzīgi, bet tipu sistēma tos uzskata par dažādiem. Tas uzlabo tipu drošību un novērš kļūdas, kas citādi varētu paslīdēt garām.
Zīmola tipu ieviešana TypeScript
Zīmola tipi tiek ieviesti, izmantojot krustojuma tipus (intersection types) un unikālu simbolu vai virknes literāli. Ideja ir pievienot tipam "zīmolu", kas to atšķir no citiem tipiem ar tādu pašu struktūru.
Simbolu izmantošana (ieteicams)
Simbolu izmantošana zīmola veidošanai parasti ir ieteicamāka, jo simboli garantēti ir unikāli.
const USD = Symbol('USD');
type USD = number & { readonly [USD]: unique symbol };
const EUR = Symbol('EUR');
type EUR = number & { readonly [EUR]: unique symbol };
function createUSD(value: number): USD {
return value as USD;
}
function createEUR(value: number): EUR {
return value as EUR;
}
function addUSD(a: USD, b: USD): USD {
return (a + b) as USD;
}
const usd1 = createUSD(10);
const usd2 = createUSD(20);
const eur1 = createEUR(15);
const totalUSD = addUSD(usd1, usd2);
console.log("Total USD:", totalUSD);
// Nākamās rindas atkomentēšana izraisīs tipa kļūdu
// const invalidOperation = addUSD(usd1, eur1);
Šajā piemērā USD
un EUR
ir zīmola tipi, kas balstīti uz number
tipa. unique symbol
nodrošina, ka šie tipi ir atšķirīgi. Funkcijas createUSD
un createEUR
tiek izmantotas, lai izveidotu šo tipu vērtības, un funkcija addUSD
pieņem tikai USD
vērtības. Mēģinājums pieskaitīt EUR
vērtību USD
vērtībai izraisīs tipa kļūdu.
Virkņu literāļu izmantošana
Jūs varat izmantot arī virkņu literāļus zīmola veidošanai, lai gan šī pieeja ir mazāk robusta nekā simbolu izmantošana, jo virkņu literāļi negarantē unikalitāti.
type USD = number & { readonly __brand: 'USD' };
type EUR = number & { readonly __brand: 'EUR' };
function createUSD(value: number): USD {
return value as USD;
}
function createEUR(value: number): EUR {
return value as EUR;
}
function addUSD(a: USD, b: USD): USD {
return (a + b) as USD;
}
const usd1 = createUSD(10);
const usd2 = createUSD(20);
const eur1 = createEUR(15);
const totalUSD = addUSD(usd1, usd2);
console.log("Total USD:", totalUSD);
// Nākamās rindas atkomentēšana izraisīs tipa kļūdu
// const invalidOperation = addUSD(usd1, eur1);
Šis piemērs sasniedz to pašu rezultātu kā iepriekšējais, bet izmanto virkņu literāļus, nevis simbolus. Lai gan tas ir vienkāršāk, ir svarīgi nodrošināt, ka zīmola veidošanai izmantotie virkņu literāļi ir unikāli jūsu koda bāzē.
Praktiski piemēri un pielietojuma gadījumi
Zīmola tipus var pielietot dažādos scenārijos, kur nepieciešams ieviest tipu drošību, kas pārsniedz strukturālo saderību.
Identifikatori (ID)
Apsveriet sistēmu ar dažādiem identifikatoru tipiem, piemēram, UserID
, ProductID
un OrderID
. Visi šie ID varētu tikt attēloti kā skaitļi vai virknes, bet jūs vēlaties novērst nejaušu dažādu ID tipu sajaukšanu.
const UserIDBrand = Symbol('UserID');
type UserID = string & { readonly [UserIDBrand]: unique symbol };
const ProductIDBrand = Symbol('ProductID');
type ProductID = string & { readonly [ProductIDBrand]: unique symbol };
function getUser(id: UserID): { name: string } {
// ... iegūst lietotāja datus
return { name: "Alice" };
}
function getProduct(id: ProductID): { name: string, price: number } {
// ... iegūst produkta datus
return { name: "Example Product", price: 25 };
}
function createUserID(id: string): UserID {
return id as UserID;
}
function createProductID(id: string): ProductID {
return id as ProductID;
}
const userID = createUserID('user123');
const productID = createProductID('product456');
const user = getUser(userID);
const product = getProduct(productID);
console.log("User:", user);
console.log("Product:", product);
// Nākamās rindas atkomentēšana izraisīs tipa kļūdu
// const invalidCall = getUser(productID);
Šis piemērs demonstrē, kā zīmola tipi var novērst ProductID
padošanu funkcijai, kas sagaida UserID
, tādējādi uzlabojot tipu drošību.
Domēnam specifiskas vērtības
Zīmola tipi var būt noderīgi arī, lai attēlotu domēnam specifiskas vērtības ar ierobežojumiem. Piemēram, jums varētu būt tips procentiem, kuriem vienmēr jābūt starp 0 un 100.
const PercentageBrand = Symbol('Percentage');
type Percentage = number & { readonly [PercentageBrand]: unique symbol };
function createPercentage(value: number): Percentage {
if (value < 0 || value > 100) {
throw new Error('Procentiem jābūt starp 0 un 100');
}
return value as Percentage;
}
function applyDiscount(price: number, discount: Percentage): number {
return price * (1 - discount / 100);
}
try {
const discount = createPercentage(20);
const discountedPrice = applyDiscount(100, discount);
console.log("Discounted Price:", discountedPrice);
// Nākamās rindas atkomentēšana izraisīs kļūdu izpildes laikā
// const invalidPercentage = createPercentage(120);
} catch (error) {
console.error(error);
}
Šis piemērs parāda, kā izpildes laikā (runtime) ieviest ierobežojumu zīmola tipa vērtībai. Lai gan tipu sistēma nevar garantēt, ka Percentage
vērtība vienmēr ir starp 0 un 100, funkcija createPercentage
var ieviest šo ierobežojumu izpildes laikā. Jūs varat arī izmantot bibliotēkas, piemēram, io-ts, lai ieviestu zīmola tipu validāciju izpildes laikā.
Datuma un laika attēlojumi
Darbs ar datumiem un laikiem var būt sarežģīts dažādu formātu un laika joslu dēļ. Zīmola tipi var palīdzēt atšķirt dažādus datuma un laika attēlojumus.
const UTCDateBrand = Symbol('UTCDate');
type UTCDate = string & { readonly [UTCDateBrand]: unique symbol };
const LocalDateBrand = Symbol('LocalDate');
type LocalDate = string & { readonly [LocalDateBrand]: unique symbol };
function createUTCDate(dateString: string): UTCDate {
// Validē, vai datuma virkne ir UTC formātā (piem., ISO 8601 ar Z)
if (!/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/.test(dateString)) {
throw new Error('Nederīgs UTC datuma formāts');
}
return dateString as UTCDate;
}
function createLocalDate(dateString: string): LocalDate {
// Validē, vai datuma virkne ir vietējā datuma formātā (piem., YYYY-MM-DD)
if (!/\d{4}-\d{2}-\d{2}/.test(dateString)) {
throw new Error('Nederīgs vietējā datuma formāts');
}
return dateString as LocalDate;
}
function convertUTCDateToLocalDate(utcDate: UTCDate): LocalDate {
// Veic laika joslas konvertāciju
const date = new Date(utcDate);
const localDateString = date.toLocaleDateString();
return createLocalDate(localDateString);
}
try {
const utcDate = createUTCDate('2024-01-20T10:00:00.000Z');
const localDate = convertUTCDateToLocalDate(utcDate);
console.log("UTC Date:", utcDate);
console.log("Local Date:", localDate);
} catch (error) {
console.error(error);
}
Šis piemērs atšķir UTC un vietējos datumus, nodrošinot, ka jūs strādājat ar pareizo datuma un laika attēlojumu dažādās lietojumprogrammas daļās. Izpildes laika validācija nodrošina, ka šiem tipiem var piešķirt tikai pareizi formatētas datuma virknes.
Labākās prakses zīmola tipu izmantošanai
Lai efektīvi izmantotu zīmola tipus TypeScript, apsveriet šādas labākās prakses:
- Izmantojiet simbolus zīmola veidošanai: Simboli nodrošina visspēcīgāko unikalitātes garantiju, samazinot tipa kļūdu risku.
- Veidojiet palīgfunkcijas: Izmantojiet palīgfunkcijas, lai izveidotu zīmola tipu vērtības. Tas nodrošina centrālu validācijas punktu un konsekvenci.
- Pielietojiet izpildes laika validāciju: Lai gan zīmola tipi uzlabo tipu drošību, tie nenovērš nepareizu vērtību piešķiršanu izpildes laikā. Izmantojiet izpildes laika validāciju, lai ieviestu ierobežojumus.
- Dokumentējiet zīmola tipus: Skaidri dokumentējiet katra zīmola tipa mērķi un ierobežojumus, lai uzlabotu koda uzturējamību.
- Apsveriet veiktspējas ietekmi: Zīmola tipi rada nelielu papildu slodzi krustojuma tipa un palīgfunkciju nepieciešamības dēļ. Apsveriet veiktspējas ietekmi koda veiktspējas kritiskajās daļās.
Zīmola tipu priekšrocības
- Uzlabota tipu drošība: Novērš nejaušu strukturāli līdzīgu, bet loģiski atšķirīgu tipu sajaukšanu.
- Uzlabota koda skaidrība: Padara kodu lasāmāku un vieglāk saprotamu, skaidri atšķirot tipus.
- Samazinātas kļūdas: Uztver potenciālās kļūdas kompilēšanas laikā, samazinot izpildes laika kļūdu risku.
- Palielināta uzturējamība: Padara kodu vieglāk uzturējamu un pārveidojamu, nodrošinot skaidru atbildības sadalījumu.
Zīmola tipu trūkumi
- Palielināta sarežģītība: Pievieno koda bāzei sarežģītību, īpaši strādājot ar daudziem zīmola tipiem.
- Izpildes laika papildu slodze: Rada nelielu papildu slodzi izpildes laikā palīgfunkciju un izpildes laika validācijas dēļ.
- Potenciāls šablona kodam: Var novest pie šablona koda (boilerplate), īpaši veidojot un validējot zīmola tipus.
Alternatīvas zīmola tipiem
Lai gan zīmola tipi ir spēcīga tehnika nominālās tipēšanas sasniegšanai TypeScript, pastāv alternatīvas pieejas, kuras jūs varētu apsvērt.
Necaurredzamie tipi (Opaque Types)
Necaurredzamie tipi ir līdzīgi zīmola tipiem, bet nodrošina skaidrāku veidu, kā paslēpt pamatā esošo tipu. TypeScript nav iebūvēta atbalsta necaurredzamiem tipiem, bet tos var simulēt, izmantojot moduļus un privātus simbolus.
Klases
Klašu izmantošana var nodrošināt objektorientētāku pieeju atšķirīgu tipu definēšanai. Lai gan TypeScript klases ir strukturāli tipētas, tās piedāvā skaidrāku atbildības sadalījumu un var tikt izmantotas, lai ieviestu ierobežojumus, izmantojot metodes.
Bibliotēkas, piemēram, `io-ts` vai `zod`
Šīs bibliotēkas nodrošina sarežģītu izpildes laika tipu validāciju un var tikt kombinētas ar zīmola tipiem, lai nodrošinātu gan kompilēšanas, gan izpildes laika drošību.
Noslēgums
TypeScript zīmola tipi ir vērtīgs rīks tipu drošības un koda skaidrības uzlabošanai strukturālā tipu sistēmā. Pievienojot tipam "zīmolu", jūs varat ieviest nominālo tipēšanu un novērst nejaušu strukturāli līdzīgu, bet loģiski atšķirīgu tipu sajaukšanu. Lai gan zīmola tipi rada zināmu sarežģītību un papildu slodzi, uzlabotas tipu drošības un koda uzturējamības priekšrocības bieži vien atsver trūkumus. Apsveriet zīmola tipu izmantošanu scenārijos, kur nepieciešams nodrošināt, ka vērtība pieder noteiktam tipam, neatkarīgi no tās struktūras.
Izprotot strukturālās un nominālās tipēšanas pamatprincipus un pielietojot šajā rakstā izklāstītās labākās prakses, jūs varat efektīvi izmantot zīmola tipus, lai rakstītu robustāku un uzturējamāku TypeScript kodu. Sākot ar valūtu un identifikatoru attēlošanu līdz pat domēnam specifisku ierobežojumu ieviešanai, zīmola tipi nodrošina elastīgu un spēcīgu mehānismu tipu drošības uzlabošanai jūsu projektos.
Strādājot ar TypeScript, izpētiet dažādās pieejamās tehnikas un bibliotēkas tipu validācijai un ieviešanai. Apsveriet zīmola tipu izmantošanu kopā ar izpildes laika validācijas bibliotēkām, piemēram, io-ts
vai zod
, lai sasniegtu visaptverošu pieeju tipu drošībai.